<template>
  <div class="">
    <div class="demo">
      <canvas id="canvas" :width="width" :height="height"></canvas>
      
      <div class="draw-btn-group">
        <div :class="{ active: drawType == '' }" title="自由选择" @click="drawTypeChange('')">
          <i class="draw-icon icon-mouse"></i>
        </div>
        <div
          :class="{ active: drawType == 'arrow' }"
          title="箭头"
          @click="drawTypeChange('arrow')"
        >
          <i class="draw-icon icon-1"></i>
        </div>
        <div
          :class="{ active: drawType == 'text' }"
          title="文本输入框"
          @click="drawTypeChange('text')"
        >
          <i class="draw-icon icon-2"></i>
        </div>
        <div
          :class="{ active: drawType == 'ellipse' }"
          title="画圆"
          @click="drawTypeChange('ellipse')"
        >
          <i class="draw-icon icon-3"></i>
        </div>
        <div
          :class="{ active: drawType == 'rectangle' }"
          title="画矩形"
          @click="drawTypeChange('rectangle')"
        >
          <i class="draw-icon icon-4"></i>
        </div>
        <div
          :class="{ active: drawType == 'polygon' }"
          title="画多边形"
          @click="drawPolygonEvent"
        >
          <i class="draw-icon icon-6"></i>
        </div>
        <div
          :class="{ active: drawType == 'pen' }"
          title="笔画"
          @click="drawTypeChange('pen')"
        >
          <i class="draw-icon icon-7"></i>
        </div>
      </div>
    </div>
    <img id="img" style="display: none;" :src="imgSrc" />
    <!-- <button @click="getCanvasJSON">获取数据</button> -->

    <div style="display: flex;display: none;">
      <div>
        <p>画笔颜色</p>
        <sketch-picker v-model="sketchColor"></sketch-picker>
      </div>
      <div>
        <p>填充色</p>
        <sketch-picker v-model="sketchFill"></sketch-picker>
      </div>
    </div>

  </div>
</template>

<script>
export default {
  data() {
    return {
      sketchColor: '', // 画笔颜色
      sketchFill: '',  // 填充色

      color: 'rgba(0, 136, 255, 1)', // 画笔颜色
      fillColor: 'rgba(0, 136, 255, 0.3)', // 填充色
      drawWidth: 1, //笔触宽度

      width: 300,
      height: 300,

      canvas: null,

      mouseFrom: {x: 0, y: 0},
      mouseTo: {x: 0, y: 0},
      drawType: null,         // 当前绘制图像的种类
      textbox: null,          // 文本绘制对象
      drawingObject: null,    // 当前绘制对象
      moveCount: 1,           // 绘制移动计数器
      doDrawing: false,       // 绘制状态

      //polygon 相关参数
      polygonMode: false,
      pointArray: [],
      lineArray: [],
      activeShape: false,
      activeLine: '',
      line: {},

      relationship: {
        x: 0,
        y: 0,
        zoom: 1
      },

      // 后台拿到的 img
      imgSrc: 'https://th.bing.com/th/id/R.4a2296fbae3eef8ccf014f7411c7ac48?rik=9rEGUpvZrHtitQ&riu=http%3a%2f%2fpic16.nipic.com%2f20110827%2f2127531_105629251000_2.jpg&ehk=BwgrEKqEmLbFhAjvQrvWj4Bd%2fuWbF9Uvu9Lh80hEzF4%3d&risl=&pid=ImgRaw&r=0',
    }
  },
  watch: {
    drawType() {
      if (this.textbox) this.textbox.exitEditing()
      this.canvas.selection = !this.drawType;
    },
    width() {
      this.canvas.setWidth(this.width);
    },
    height() {
      this.canvas.setHeight(this.height);
    },
    sketchColor(val) {
      const r = val.rgba
      this.color = `rgba(${r.r}, ${r.g}, ${r.b}, ${r.a})`
    },
    sketchFill(val) {
      const r = val.rgba
      this.fillColor = `rgba(${r.r}, ${r.g}, ${r.b}, ${r.a})`
    }
  },
  mounted() {
    this.loadImg()
  },
  methods: {
    loadImg() { // 加载之前上传的图片
      let img = document.createElement('img')
      img.setAttribute('src', this.imgSrc)
      img.setAttribute('id', 'hidden_img')
      img.style.display = 'none'
      document.body.appendChild(img)
      img.onload = () => {
        this.width = document.querySelector('body').offsetWidth
        this.height = document.querySelector('body').offsetHeight

        // this.width = img.width
        // this.height = img.height

        this.initCanvas()

        const imgInstance = new fabric.Image(img, {
          zIndex: -1,
          selectable: false,
        });
        this.canvas.add(imgInstance)
      }
    },
    initCanvas() { // 初始化 canvas
      this.canvas = new fabric.Canvas('canvas', {})

      // 添加监听事件
      this.canvas.on("mouse:down", this.userMouseDown)
      this.canvas.on("mouse:move", this.userMouseMove)
      this.canvas.on("mouse:up", this.userMouseUp)
      this.canvas.on('selection:created', this.selectionObject)

      console.log('画布中心点', this.canvas.getCenter())
      
      // ctrl+滚轮 画布缩放
      this.canvas.on('mouse:wheel', opt => {
        const e = opt.e
        if (e.ctrlKey || e.metaKey) {
          let zoom = (e.deltaY > 0 ? 0.1 : -0.1) + this.canvas.getZoom()
          zoom = Math.max(0.1, zoom)
          zoom = Math.min(5, zoom)
          if (zoom == this.relationship.zoom) return
          const [x, y] = [e.offsetX, e.offsetY]

          this.relationship.x += x / zoom - x / this.relationship.zoom
          this.relationship.y += y / zoom - y / this.relationship.zoom
          const zoomPoint = new fabric.Point(x, y)
          this.canvas.zoomToPoint(zoomPoint, zoom)
          this.relationship.zoom = zoom
        }
      })

      // 撤销
      window.onkeydown = e => {
        const code = e.keyCode || e.which || e.charCode
        const key = e.ctrlKey || e.metaKey

        // 删除键
        if (code == 8 || code == 46) this.deleteObj()

        // ctrl + z
        if (key && code == 90) {
          this.canvas.remove(
            this.canvas.getObjects()[this.canvas.getObjects().length - 1]
          )
        }
      }
    },

    getCanvasJSON() { // 获取所有的图形数据
      console.log('---', this.canvas.toJSON())
      console.log('============================')
      console.log(JSON.stringify(this.canvas))
    },
    selectionObject(e) { // 点击时 获取选中的图形对象
      // target 为图形对象的全部信息  可通过 loadFromJSON 方法回显
      const target = e.target
      console.log(JSON.stringify(e), e)
    },
    drawTypeChange(type) { // 画笔种类变化
      this.drawType = type
      // skipTargetFind 默认false。设置为true时，当鼠标覆盖在Canvas上时，对象的探测均会失效。这个属性可用于提高性能。
      this.canvas.skipTargetFind = !!type
      // When true, mouse events on canvas (mousedown/mousemove/mouseup) result in free drawing. After mousedown, mousemove creates a shape, and then mouseup finalizes it and adds an instance of `fabric.Path` onto canvas.
      this.canvas.isDrawingMode = type == 'pen' ? true : false

    },
    deleteObj() { // 删除图形对象
      this.canvas.getActiveObjects().map(item => {
        this.canvas.remove(item)
      })
    },

    drawing(e) { // 画图方法
      if (this.drawingObject) this.canvas.remove(this.drawingObject)

      let canvasObject = null,
          left = this.mouseFrom.x,
          top  = this.mouseFrom.y,
          mouseFrom = this.mouseFrom,
          mouseTo   = this.mouseTo
      switch(this.drawType) {
        case 'arrow': // 箭头
          let x1 = mouseFrom.x,
              x2 = mouseTo.x,
              y1 = mouseFrom.y,
              y2 = mouseTo.y
          let w = x2 - x1,
              h = y2 -y1,
              sh = Math.cos(Math.PI / 4) * 16
          
          let sin = h / Math.sqrt(Math.pow(w, 2) + Math.pow(h, 2))
          let cos = w / Math.sqrt(Math.pow(w, 2) + Math.pow(h, 2))
          let w1 = (16 * sin) / 4,
              h1 = (16 * cos) / 4,
              centerx = sh * cos,
              centery = sh * sin
          // centerx,centery 表示起始点，终点连线与箭头尖端等边三角形交点相对x，y
          // w1 ，h1用于确定四个点
          var path = ` 
            M ${x1} ${y1} 
            L ${x2 - centerx + w1} ${y2 - centery - h1} 
            L ${x2 - centerx + w1 * 2} ${y2 - centery - h1 * 2} 
            L ${x2} ${y2} 
            L ${x2 - centerx - w1 * 2} ${y2 - centery + h1 * 2} 
            L ${x2 - centerx - w1} ${y2 - centery + h1} 
            Z
          `
          canvasObject = new fabric.Path(path, {
            stroke: this.color,
            fill: this.color,
            strokeWidth: this.drawWidth,
            hasControls: false
          })
          break
        case 'rectangle': // 长方形
          // if (e.e.shiftKey) { // 按住shift为正方形
          //   mouseTo.x - left > mouseTo.y - top ? (mouseTo.y = top + mouseTo.x - left) : (mouseTo.x = left + mouseTo.y - top)
          // }
          // var path = `
          //   M ${mouseFrom.x} ${mouseFrom.y} 
          //   L ${mouseTo.x} ${mouseFrom.y} 
          //   L ${mouseTo.x} ${mouseTo.y} 
          //   L ${mouseFrom.x} ${mouseTo.y} 
          //   L ${mouseFrom.x} ${mouseFrom.y} 
          //   z
          // `
          // canvasObject = new fabric.Path(path, {
          //   left: left,
          //   top: top,
          //   stroke: this.color,
          //   strokeWidth: this.drawWidth,
          //   fill: this.fillColor,
          //   hasControls: false
          // })
          console.log('---------', left, top)
          
          // console.log('---------')
          canvasObject = new fabric.Rect({
            left,
            top,
            width: this.mouseTo.x - this.mouseFrom.x,
            height: this.mouseTo.y - this.mouseFrom.y,
            stroke: this.color,
            strokeWidth: this.drawWidth,
            fill: this.fillColor,
            hasControls: false,
          })
          break
        case 'ellipse': // 椭圆
          if (e.e.shiftKey) {
            mouseTo.x - left > mouseTo.y - top ? (mouseTo.y = top + mouseTo.x - left) : (mouseTo.x = left + mouseTo.y - top)
          }
          canvasObject = new fabric.Ellipse({
            left: (mouseTo.x - left) / 2 + left,
            top: (mouseTo.y - top) / 2 + top,
            stroke: this.color,
            fill: this.fillColor,
            originX: "center",
            originY: "center",
            rx: Math.abs(left - mouseTo.x) / 2,
            ry: Math.abs(top - mouseTo.y) / 2,
            strokeWidth: this.drawWidth,
            hasControls: false,
          })
          break
        case 'text':
          this.textbox = new fabric.Textbox("", {
            left: mouseFrom.x,
            top: mouseFrom.y - 10,
            fontSize: 16,
            borderColor: this.color,
            fill: this.color,
            hasControls: false,
            borderColor: this.sketchColor
          })
          this.canvas.add(this.textbox)
          this.textbox.enterEditing() // 进入编辑状态
          this.textbox.hiddenTextarea.focus()
          break
        default:
          break
      }
      if (canvasObject) {
        this.canvas.add(canvasObject)
        this.drawingObject = canvasObject
      }
    },
    drawPolygonEvent() { // 用户触发绘制多边形
      this.drawType = "polygon"
      this.polygonMode = true

      this.pointArray = new Array() // 顶点集合
      this.lineArray = new Array()  // 线集合
      this.canvas.isDrawingMode = false
    },
    drawPolygon(e) { // 绘制不规则多边形 （使用 点/线/多边形填充 组合成的）
      let random = Math.floor(Math.random() * 10000)
      let id = new Date().getTime() + random

      let circle = new fabric.Circle({
        radius: 5,
        fill: this.fillColor,
        stroke: 'red',
        strokeWidth: this.drawWidth,
        left: (e.pointer.x || e.e.layerX) / this.canvas.getZoom(),
        top: (e.pointer.y || e.e.layerY) / this.canvas.getZoom(),
        selectable: false,
        hasBorders: false,
        hasControls: false,
        originX: "center",
        originY: "center",
        id: id,
        isPolygonCircle: true,
        objectCaching: false
      })

      if (!this.pointArray.length) circle.set({ fill: 'red' })
      let points = [
        (e.pointer.x || e.e.layerX) / this.canvas.getZoom(),
        (e.pointer.y || e.e.layerY) / this.canvas.getZoom(),
        (e.pointer.x || e.e.layerX) / this.canvas.getZoom(),
        (e.pointer.y || e.e.layerY) / this.canvas.getZoom(),
      ]
      this.line = new fabric.Line(points, {
        strokeWidth: this.drawWidth,
        fill: this.fillColor,
        stroke: this.sketchColor,
        class: "line",
        originX: "center",
        originY: "center",
        selectable: false,
        hasBorders: false,
        hasControls: false,
        evented: false,
        objectCaching: false,
      })

      if (!this.activeShape) {
        let polyPoint = [
          {
            x: (e.pointer.x || e.e.layerX) / this.canvas.getZoom(),
            y: (e.pointer.y || e.e.layerY) / this.canvas.getZoom(),
          }
        ]
        let polygon = new fabric.Polygon(polyPoint, {
          stroke: this.sketchColor,
          strokeWidth: this.drawWidth,
          fill: this.fillColor,
          opacity: 0.5,
          selectable: false,
          hasBorders: false,
          hasControls: false,
          evented: false,
          objectCaching: false,
        })
        this.activeShape = polygon
        this.canvas.add(polygon)
      } else {
        let pos = this.canvas.getPointer(e.e)
        let points = this.activeShape.get("points")
        points.push({ x: pos.x, y: pos.y })
        let polygon = new fabric.Polygon(points, {
          stroke: this.sketchColor,
          strokeWidth: this.drawWidth,
          fill: this.fillColor,
          opacity: 0.5,
          selectable: false,
          hasBorders: false,
          hasControls: false,
          evented: false,
          objectCaching: false,
        })
        this.canvas.remove(this.activeShape)
        this.canvas.add(polygon)
        this.activeShape = polygon
        this.canvas.renderAll()
      }

      this.activeLine = this.line
      this.pointArray.push(circle)
      this.lineArray.push(this.line)
      this.canvas.add(circle)
    },
    generatePolygon() { // 正式生成 不规则多边形 - 删除绘制过程中的图形
      let points = new Array()
      this.pointArray.map(i => {
        points.push({ x: i.left, y: i.top })
        // 删除点信息
        this.canvas.remove(i)
      })
      this.lineArray.map(i => this.canvas.remove(i))
      this.canvas.remove(this.activeShape).remove(this.activeLine)
      let polygon = new fabric.Polygon(points, {
        stroke: this.color,
        strokeWidth: this.drawWidth,
        fill: this.fillColor,
        opacity: 1,
        hasBorders: true,
        hasControls: false,
      })
      this.canvas.add(polygon)

      this.activeLine = null
      this.activeShape = null
      this.polygonMode = false
      this.doDrawing = false
      this.drawType = null
    },

    userMouseDown(e) { // 鼠标按下
      let xy = e.pointer
      // this.mouseFrom.x = xy.x
      // this.mouseFrom.y = xy.y
      this.mouseFrom.x = xy.x / this.relationship.zoom + this.relationship.x
      this.mouseFrom.y = xy.y / this.relationship.zoom + this.relationship.y
      this.doDrawing = true
      if (this.drawType == 'text') this.drawing()

      if (this.textbox) {
        this.textbox.enterEditing()
        this.textbox.hiddenTextarea.focus()
      }

      // 绘制多边形
      if (this.drawType == 'polygon') {
        // 目前不解开探测的话 会导致无法闭合，解开探测则在绘制过程中会干扰其他的图形
        this.canvas.skipTargetFind = false
        try {
          // 此段为判断是否闭合多边形，点击红点时闭合多边形
          if (this.pointArray.length > 1) {
            if (e.target && e.target.id == this.pointArray[0].id) { // 点击了初始红点
              this.generatePolygon()
            }
          }
          //未点击红点则继续作画
          if (this.polygonMode) this.drawPolygon(e)
        } catch (error) {
          console.log(error)
        }
      }
    },
    userMouseUp(e) { // 鼠标松开
      let xy = e.pointer
      // this.mouseTo.x = xy.x
      // this.mouseTo.y = xy.y

      this.mouseTo.x = xy.x / this.relationship.zoom + this.relationship.x
      this.mouseTo.y = xy.y / this.relationship.zoom + this.relationship.y

      this.drawingObject = null
      this.moveCount = 1
      if (this.drawType != 'polygon') this.doDrawing = false
    },
    userMouseMove(e) { // 鼠标移动
      if (this.moveCount % 2 && !this.doDrawing) return

      // shift + 鼠标左键  移动画布
      if (e.e.shiftKey && e && e.e) {
        this.drawType = null

        const delta = new fabric.Point(e.e.movementX, e.e.movementY)
        this.canvas.relativePan(delta)

        this.relationship.x += e.e.movementX / this.relationship.zoom
        this.relationship.y += e.e.movementY / this.relationship.zoom
      }

      this.moveCount++
      let xy = e.pointer
      // this.mouseTo.x = xy.x
      // this.mouseTo.y = xy.y
      this.mouseTo.x = xy.x / this.relationship.zoom + this.relationship.x
      this.mouseTo.y = xy.y / this.relationship.zoom + this.relationship.y

      if (this.drawType != "text" || this.drawType != "polygon") this.drawing(e)
      if (this.drawType == "polygon") {
        if (this.activeLine && this.activeLine.class == "line") {
          var pointer = this.canvas.getPointer(e.e)
          this.activeLine.set({ x2: pointer.x, y2: pointer.y })

          var points = this.activeShape.get("points")
          points[this.pointArray.length] = {
            x: pointer.x,
            y: pointer.y,
            zIndex: 1,
          }
          this.activeShape.set({ points: points })
          this.canvas.renderAll()
        }
        this.canvas.renderAll()
      }
    },
  }
}
</script>
<style lang="less" scoped>
*{
  margin: 0;
  padding: 0;
}
.demo {
  width: 100%;
  height: 100vh;
}
.draw-btn-group {
  top: 10px;
  left: 10px;
  z-index: 1;
  background: #fff;
  position: absolute;
  // width: 1270px;
  margin-top: 10px;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: flex-start;
  & > div {
    background: #fafafa;
    cursor: pointer;
    &:hover {
      background: #eee;
    }
    i {
      display: flex;
      background-repeat: no-repeat;
      background-size: 80%;
      background-position: 50% 50%;
      height: 30px;
      width: 30px;
    }
    .icon-1 {
      background-image: url("../assets/icons/draw/1.png");
    }
    .icon-pentagram {
      background-image: url("../assets/icons/draw/pentagram.png");
    }
    .icon-2 {
      background-image: url("../assets/icons/draw/2.png");
    }
    .icon-3 {
      background-image: url("../assets/icons/draw/3.png");
    }
    .icon-4 {
      background-image: url("../assets/icons/draw/4.png");
      background-size: 75%;
    }
    .icon-5 {
      background-image: url("../assets/icons/draw/5.png");
      background-size: 70%;
    }
    .icon-6 {
      background-image: url("../assets/icons/draw/6.png");
    }
    .icon-7 {
      background-image: url("../assets/icons/draw/7.png");
      background-size: 80%;
    }
    .icon-del {
      background-image: url("../assets/icons/draw/del.png");
      background-size: 90%;
    }
    .icon-img {
      background-image: url("../assets/icons/draw/img.png");
      background-size: 80%;
    }
    .icon-back {
      background-image: url("../assets/icons/draw/back.png");
      background-size: 75%;
    }
    .icon-save {
      background-image: url("../assets/icons/draw/save.png");
      background-size: 80%;
    }
    .icon-mouse {
      background-image: url("../assets/icons/draw/mouse.png");
      background-size: 60%;
    }
  }
  .active {
    background: red;
  }
}
</style>